/* ------------------------------------------------------------------------*
 *
 * ------------------------------------------------------------------------*/
#include "os_obj.h"
#include "bsp_spi_nor.h"
#include "nor_cmd.h"
#include "data_map.h"
#include "nand_lut.h"
#include "nand_w25n.h"
#include "stdlib.h"
#include "string.h"
#include "mytype.h"
#include "systask.h"
#include "rtctime.h"
#include "flashtoeep.h"
#include "common.h"
#include "str_ext.h"
#include "bsp_api.h"

void nand_lut_write_all(void);
void nand_lut_read_all(void);
void nand_spi_check(void);
void nand_qpi_check(void);
void nand_lut_check(void);

/****************************************************************************
* @brief   delay .
*****************************************************************************/

typedef struct
{
	uint16_t wr_req;			// 从page指定页开始写入数据dat，如果页非空跳过
	uint16_t rd_req;			// 从page指定页开始读取数据
	uint16_t er_req;			// 从blocks指定块开始擦除块
	uint16_t lut_req;			// 重建LUT
	uint16_t check_req;			// 0空闲 1-spi 2-qpi 3-lut 全写全读验证
	uint16_t cmd_req;
	
	uint8_t nand_fill_req;		// 全盘写入数据-lut
	uint8_t nand_read_req;		// 读取全盘数据-lut

	uint8_t *wr_buff;
	uint8_t *rd_buff;
	dword_tt *dw_buff;

	uint8_t nand_id[8];

	uint32_t blocks;
	uint32_t pages;
	uint32_t len;

	uint32_t err;
	uint32_t dat;
	uint32_t opt_time;
	
	uint8_t st[4];
	uint8_t ecc_err;
	uint8_t Uid[16][32];
	uint8_t Parameter[2][256];
} nand_test_type;

nand_test_type nand_tst;



/*-----------------------------------------------------------------------------------------------------*
* nand lut write data get
*-----------------------------------------------------------------------------------------------------*/
uint8_t nand_fill_dat_get(uint32_t addr_offset, uint8_t *pdat, uint16_t len)
{
	uint32_t *wdat;
	uint32_t dat;

	wdat = (uint32_t *)pdat;

	dat = nand_tst.dat + addr_offset;

	len >>= 2;
	while (len--)
	{
		*wdat++ = dat;
		dat += 4;
	}

	return 0;
}

/*-----------------------------------------------------------------------------------------------------*
* nand lut write data get for lut check
*-----------------------------------------------------------------------------------------------------*/
uint8_t nand_check_dat_get(uint32_t addr_offset, uint8_t *pdat, uint16_t len)
{
	uint32_t *wdat;
	uint32_t dat;

	wdat = (uint32_t *)pdat;

	dat = nand_tst.dat + addr_offset;

	len >>= 2;
	while (len--)
	{
		*wdat++ = dat;
		dat += 4;
	}

	return 0;
}

/*------------------------------------------------------------------------------*
* nand test
*-------------------------------------------------------------------------------*/
void nand_flash_test(void)
{
	bsp_qpi_port_init();
	nor_spi_init();
	
	gd25_dbg_print("switch to nand test module \n");

	spi_nand_read_id( nand_tst.nand_id);

	// 申请内存
	nand_tst.wr_buff = malloc(2048 + 16);
	nand_tst.rd_buff = malloc(2048 + 16);
	nand_tst.dw_buff = malloc(4096 + 16);

	
	
	// 初始化
	nand_tst.dat = 0x80;
	nand_tst.pages = 1;
	nand_tst.len = 1;
	nand_tst.wr_req = 0;

	// 读状态
	nand_tst.st[0] = spi_nand_featute_get( NAND_FEATURE_1_PROTECT);  //测试读取的默认值是0x7C
	nand_tst.st[1] = spi_nand_featute_get( NAND_FEATURE_2_CFG);
	nand_tst.st[2] = spi_nand_featute_get( NAND_FEATURE_3_ST);

	// 设置
	if (nand_tst.st[0] != 0)
	{
		spi_nand_write_enable();
		spi_nand_featute_set( NAND_FEATURE_1_PROTECT, 0xFF, 0);
		nand_tst.st[0] = spi_nand_featute_get( NAND_FEATURE_1_PROTECT);
	}

	// 读选项字节
	w25n_opt_mem_select();
	//Unique ID
	spi_nand_read_page( 0);
	spi_nand_q_read_cache(0, nand_tst.Uid[0], 32 * 4);
	//Parameter Page
	spi_nand_read_page( 1);
	spi_nand_q_read_cache(0, nand_tst.Parameter[0], 256 * 2);

	w25n_main_mem_select();

	// lut 初始化
	nand_lut_open();

	for (;;)
	{
		if(sys_st.str_module != 2)
		{
			break;
		}
		
		osDelay(10);

		//写入指定物理页
		while (nand_tst.wr_req)
		{
			uint16_t bempty = 1;
			uint16_t cnt;
			//读取判定
			spi_nand_read_page( nand_tst.pages);
			spi_nand_read_cache( 0, nand_tst.rd_buff, 2048);
			if (0 != comp_mem_dat_u8(nand_tst.rd_buff, 0xFF, 2048))
				bempty = 0;

			if (bempty)
			{
				memset(nand_tst.wr_buff, nand_tst.dat, 2048);
				spi_nand_write_cache( 0, nand_tst.wr_buff, 2048);
				spi_nand_program_exe( nand_tst.pages);
			}

			nand_tst.wr_req--;
			nand_tst.pages++;
			nand_tst.dat++;
		}

		//读取指定物理页
		while (nand_tst.rd_req)
		{
			spi_nand_read_page( nand_tst.pages);
			nand_tst.ecc_err = spi_nand_ecc_error_get();
			spi_nand_read_cache( 0, nand_tst.rd_buff, 2048);
			nand_tst.rd_req--;
			nand_tst.pages++;
		}

		//擦除指定物理块
		while (nand_tst.er_req)
		{
			spi_nand_block_erase( nand_tst.blocks);
			nand_tst.er_req--;
			nand_tst.blocks++;
		}

		//全盘写入
		if (nand_tst.nand_fill_req)
		{
			nand_lut_write_all();
			nand_tst.nand_fill_req = 0;
		}

		//全盘读取
		if (nand_tst.nand_read_req)
		{
			nand_lut_read_all();
			nand_tst.nand_read_req = 0;
		}

		//spi mode
		if (1 == nand_tst.check_req)
		{
			nand_spi_check();
			nand_tst.check_req = 0;
		}

		//qpi mode
		if (2 == nand_tst.check_req)
		{
			nand_qpi_check();
			nand_tst.check_req = 0;
		}

		//flt mode
		if (3 == nand_tst.check_req)
		{
			nand_lut_check();
			nand_tst.check_req = 0;
		}

		//重建LUT表测试
		if (nand_tst.lut_req)
		{
			nand_tst.lut_req = 0;
			nand_lut_creat();
		}
		
		// cmd 测试
		if(nand_tst.cmd_req)
		{
			copy_u8("12345678", nand_tst.wr_buff, 8);
			nand_tst.cmd_req = 0;
			spi_nand_block_erase( 0x3C0);
			
			spi_nand_write_cache(0, nand_tst.wr_buff, 8);
			spi_nand_write_cache(16, nand_tst.wr_buff, 8);
			spi_nand_program_exe(0x3C0 * 64 + 0);
			
			spi_nand_write_cache(32, nand_tst.wr_buff, 8);
			spi_nand_program_exe(0x3C0 * 64 + 1);
			
			spi_nand_random_write_cache(48, nand_tst.wr_buff, 8);
			spi_nand_program_exe(0x3C0 * 64 + 2);
			
			spi_nand_random_write_cache(64, nand_tst.wr_buff, 8);
			spi_nand_program_exe(0x3C0 * 64 + 3);
		}
	}

	free(nand_tst.wr_buff);
	free(nand_tst.rd_buff);
	free(nand_tst.dw_buff);
	nand_lut_close();
}

/*------------------------------------------------------------------------------*
* nand read check
*-------------------------------------------------------------------------------*/
void nand_lut_write_all(void)
{
	gd25_dbg_print("nand fill start \n");
	for (nand_tst.blocks = 0; nand_tst.blocks < nand_dev.logic_block_amount; nand_tst.blocks++)
	{
		nand_lut_block_write(nand_tst.blocks, nand_fill_dat_get);

		if (nand_tst.nand_fill_req == 2)
		{
			break;
		}

		if (0 == (nand_tst.blocks & 0x3F))
		{
			gd25_dbg_print("nand fill %d %% \n", nand_tst.blocks * 100 / nand_dev.logic_block_amount);
		}
	}

	gd25_dbg_print("nand fill end \n");
}

/*------------------------------------------------------------------------------*
* nand read check
*-------------------------------------------------------------------------------*/
void nand_lut_read_all(void)
{
	uint32_t tm_start;
	
	gd25_dbg_print("nand read all start \n");
	
	tm_start = sys_st.ticks;
	
	for (nand_tst.pages = 0; nand_tst.pages < nand_dev.logic_block_amount * nand_dev.page_per_block; nand_tst.pages++)
	{

		nand_lut_page_read(nand_tst.pages, nand_tst.rd_buff, 1);

		if (nand_tst.nand_read_req == 2)
		{
			break;
		}
	}
	
	nand_tst.opt_time = sys_st.ticks - tm_start;
	
	gd25_dbg_print("nand read all end \n");
}

/*------------------------------------------------------------------------------*
* nand spi check
*-------------------------------------------------------------------------------*/
void nand_spi_check(void)
{
	uint32_t dats = nand_tst.dat = 5;

	gd25_dbg_print("nand spi fill start \n");

	for (nand_tst.pages = 0; nand_tst.pages < nand_dev.page_per_block * nand_dev.logic_block_amount; nand_tst.pages++)
	{
		if (nand_tst.pages % nand_dev.page_per_block == 0)
		{
			spi_nand_block_erase( nand_tst.pages / nand_dev.page_per_block);
		}

		for (int index = 0; index < nand_dev.page_main_size / 4; index++)
		{
			nand_tst.dw_buff[index].uVal = nand_tst.dat++;
		}

		spi_nand_write_cache( 0, nand_tst.dw_buff[0].v, nand_dev.page_main_size);
		spi_nand_program_exe( nand_tst.pages);
	}
	gd25_dbg_print("nand spi fill end \n");

	nand_tst.dat = dats;

	for (nand_tst.pages = 0; nand_tst.pages < nand_dev.page_per_block * nand_dev.logic_block_amount; nand_tst.pages++)
	{
		spi_nand_read_page( nand_tst.pages);
		spi_nand_read_cache( 0, nand_tst.dw_buff[0].v, nand_dev.page_main_size);

		for (int index = 0; index < nand_dev.page_main_size / 4; index++)
		{
			if (nand_tst.dw_buff[index].uVal != nand_tst.dat)
			{
				nand_tst.err++;
			}
			nand_tst.dat++;
		}

		if (nand_tst.err)
		{
			gd25_dbg_print("nand spi check err: %d @ %x \n", nand_tst.err, nand_tst.pages);
			nand_tst.err = 0;
		}
	}

	gd25_dbg_print("nand spi check end \n");
}

/*------------------------------------------------------------------------------*
* nand qpi check
*-------------------------------------------------------------------------------*/
void nand_qpi_check(void)
{
	uint32_t dats = nand_tst.dat = 5;
	gd25_dbg_print("nand qpi fill start \n");
	for (nand_tst.pages = 0; nand_tst.pages < nand_dev.page_per_block * nand_dev.logic_block_amount; nand_tst.pages++)
	{
		if (nand_tst.pages % nand_dev.page_per_block == 0)
		{
			spi_nand_block_erase( nand_tst.pages / nand_dev.page_per_block);
		}

		for (int index = 0; index < nand_dev.page_main_size / 4; index++)
		{
			nand_tst.dw_buff[index].uVal = nand_tst.dat++;
		}

		spi_nand_q_write_cache( 0, nand_tst.dw_buff[0].v, nand_dev.page_main_size);
		spi_nand_program_exe( nand_tst.pages);
	}
	gd25_dbg_print("nand qpi fill end \n");
	nand_tst.dat = dats;

	for (nand_tst.pages = 0; nand_tst.pages < nand_dev.page_per_block * nand_dev.logic_block_amount; nand_tst.pages++)
	{
		spi_nand_read_page( nand_tst.pages);
		spi_nand_q_read_cache( 0, nand_tst.dw_buff[0].v, nand_dev.page_main_size);

		for (int index = 0; index < nand_dev.page_main_size / 4; index++)
		{
			if (nand_tst.dw_buff[index].uVal != nand_tst.dat)
			{
				nand_tst.err++;
			}
			nand_tst.dat++;
		}
		if (nand_tst.err)
		{
			gd25_dbg_print("nand qpi check err: %d @ %x \n", nand_tst.err, nand_tst.pages);
			nand_tst.err = 0;
		}
	}

	gd25_dbg_print("nand qpi check end \n");

}

/*------------------------------------------------------------------------------*
* nand lut check
*-------------------------------------------------------------------------------*/
void nand_lut_check(void)
{
	uint32_t dats = nand_tst.dat = 5;
	uint32_t tick;
	
	tick = app_tick_get();
	gd25_dbg_print("nand lut fill start @ %10d\n", tick);

	for (nand_tst.pages = 0; nand_tst.pages < nand_dev.page_per_block * nand_dev.logic_block_amount; nand_tst.pages++)
	{
		if (nand_tst.pages % nand_dev.page_per_block == 0)
		{
			nand_lut_block_write(nand_tst.pages / nand_dev.page_per_block, nand_check_dat_get);
			nand_tst.dat += nand_dev.page_per_block * nand_dev.page_main_size;

			if (0 == (nand_tst.pages & 0x0FFF))
			{
				gd25_dbg_print("nand lut fill %d %% \n", nand_tst.pages * 100 / (nand_dev.page_per_block * nand_dev.logic_block_amount));
			}
		}
	}
	
	gd25_dbg_print("nand lut fill end @ %10d , time used: %d ms\n", app_tick_get(), app_tick_get() - tick);
	tick = app_tick_get();
	nand_tst.dat = dats;

	for (nand_tst.pages = 0; nand_tst.pages < nand_dev.page_per_block * nand_dev.logic_block_amount; nand_tst.pages++)
	{
		nand_lut_page_read(nand_tst.pages, nand_tst.dw_buff[0].v, 1);

		for (int index = 0; index < nand_dev.page_main_size / 4; index++)
		{
			if (nand_tst.dw_buff[index].uVal != nand_tst.dat)
			{
				nand_tst.err++;
			}
			nand_tst.dat += 4;
		}

		if (0 == (nand_tst.pages & 0x0FFF))
		{
			gd25_dbg_print("nand lut check %d %% \n", nand_tst.pages * 100 / (nand_dev.page_per_block * nand_dev.logic_block_amount));
		}

		if (nand_tst.err)
		{
			gd25_dbg_print("nand lut check err: %d @ %x \n", nand_tst.err, nand_tst.pages);
			nand_tst.err = 0;
		}
	}

	if(0 == nand_tst.err)
		gd25_dbg_puts("nand lut check err none\n");
	
	gd25_dbg_print("nand lut check end @ %10d , time used: %d ms\n", app_tick_get(), app_tick_get() - tick);
}


